package org.swift.mashup.engine.concurrency.execution;

import net.sf.saxon.Configuration;
import net.sf.saxon.om.DocumentInfo;
import net.sf.saxon.query.DynamicQueryContext;
import net.sf.saxon.query.StaticQueryContext;
import net.sf.saxon.query.XQueryExpression;
import net.sf.saxon.trans.XPathException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.swift.commons.xml.XmlUtils;
import org.swift.mashup.engine.concurrency.memory.MemoryItem;
import org.swift.mashup.engine.model.NamespaceResolverImpl;

import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import java.io.StringReader;
import java.util.BitSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

public class Condition {
	private static Log log = LogFactory.getLog(Condition.class);
	private String expression;
	private BitSet mask = new BitSet();
	private XQueryExpression condition = null;
	private Collection<String> parameters = null;
	private Map<String,String> namespaces = null;

	public Condition(String expression, Map<String,String> namespaces) {
		copyNamespaces(namespaces);
		this.setExpression(expression);
	}

	public Condition(String expression, Collection<String> parameters, Map<String,String> namespaces) {
		copyNamespaces(namespaces);
		this.setExpression(expression);
		this.parameters = parameters;
		buildNewCondition(expression, parameters);
	}
	
	public Condition(String expression, Collection<String> parameters, BitSet mask, Map<String,String> namespaces) {
		copyNamespaces(namespaces);
		this.setExpression(expression);
		this.parameters = parameters;
		this.mask = mask;
		buildNewCondition(expression, parameters);
	}

	private void copyNamespaces(Map<String, String> namespaces) {
		this.namespaces = new HashMap<String,String>();
		if ( namespaces != null ) {
			this.namespaces.putAll(namespaces);
		}
	}

	public String getExpression() {
		return expression;
	}

	public void setExpression(String expression) {
		this.expression = expression;
	}

	public BitSet getMask() {
		return mask;
	}

	private void buildNewCondition(String expression,
			Collection<String> parameters) {
		this.condition = null;
		// compile the XQuery expression
		XQueryExpression exp = null;
		// create a Configuration object
		Configuration C = new Configuration();
		// static and dynamic context
		StaticQueryContext SQC = new StaticQueryContext(C);
		SQC.setExternalNamespaceResolver( new NamespaceResolverImpl(this.namespaces) );

		try {

			// compilation
			String newExpression = "\n";
			for (String parameter : parameters) {
				newExpression = newExpression + "declare variable $"
						+ parameter + " external;\n";
			}
			newExpression = newExpression + "for $doc in . return (";
			newExpression = newExpression + expression;
			newExpression = newExpression + ")";
			log.debug(newExpression);
			exp = SQC.compileQuery(newExpression);
			this.condition = exp;
		} catch (net.sf.saxon.trans.XPathException e) {
			throw new RuntimeException(e);
		}
	}

	public boolean evaluate(Map<String, MemoryItem> parameters) {
		boolean ret = false;
		if (this.condition == null)
			return true;
		Source source = new StreamSource(new StringReader(
				"<condition></condition>"));
		StaticQueryContext SQC = this.condition.getStaticContext()
				.getUserQueryContext();
		DocumentInfo DI;
		try {
			DI = SQC.buildDocument(source);
			DynamicQueryContext DQC = new DynamicQueryContext(SQC
					.getConfiguration());
			DQC.setContextItem(DI);
			for (Entry<String, MemoryItem> entry : parameters.entrySet()) {
				MemoryItem item = entry.getValue();
				String strValue = item.toString();
				if (XmlUtils.isXml(strValue)) {
					DQC.setParameter(entry.getKey(), item.toNode());
				} else {
					DQC.setParameter(entry.getKey(), strValue);
				}
			}
			// evaluating
			ret = (Boolean) this.condition.evaluateSingle(DQC);
		} catch (XPathException e) {
			throw new RuntimeException(e);
		}
		return ret;
	}

	public XQueryExpression getCondition() {
		return condition;
	}

	public Collection<String> getParameters() {
		return parameters;
	}

	@Override
	public String toString() {
		return "exp [" + this.getExpression() + "]";
	}

	public Map<String, String> getNamespaces() {
		return namespaces;
	}
}
